/**
 *  This class creates polynomials required to create linear equations. 
 *  
 */
package tools;

import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.ArrayRealVector;
import org.apache.commons.math3.linear.DecompositionSolver;
import org.apache.commons.math3.linear.LUDecomposition;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.linear.RealVector;


/**
 * This class creates the polynomial skeleton.   
 * 
 * @author Vivek
 *
 */
public class CreatePolynomial {

	
	private int outputVars;
	private int numPoints;
	private double[][] coef;   // contains that coeficients 
	private int[] degree;
	RealVector constants;
	
	/**
	 * @param outputVars
	 * @param numPoints
	 */
	public CreatePolynomial(int outputVars, int numPoints) {
		this.outputVars = outputVars;
		this.numPoints = numPoints;
		coef = new double[outputVars][this.computeDegree()];
		degree = new int[outputVars];
		this.createEquations();
	}
	

	/**
	 *  Creates the series of polynomial equations that can be later used to 
	 *  create linear equations
	 */
	private void createEquations() {
		
		for(int l = 0;l < coef[0].length;l++ ){
			coef[0][l] = 1;
		}
		
		degree[0] = this.computeDegree();
		
		
		for (int n = 1;n < coef.length;n++){
//			System.out.println("n="+n+" degree[n]"+degree[n]);
			degree[n] = degree[n-1];
			for(int l = 1;l < degree[n];l++ ){
				coef[n][l-1]= coef[n-1][l]*l;
			}
			degree[n] = degree[n] - 1;
		}
	}


	/**
	 * Returns that degree of polynomial required.
	 * 
	 */
	private int computeDegree() {
		return outputVars*numPoints;
	}

	
	public void displayEquations(){
		for (int n = 0;n < coef.length;n++){
			String s = "";
			for(int l = 0;l < coef[n].length-1-n;l++ ){
				s += coef[n][l]+"*X^" + l+" + ";
			}
			s += coef[n][coef[n].length-1-n]+"*X^" + (coef[n].length-1-n);
			System.out.println(s);
		}		
	}
	
	public void createMatrix(double[][] xy){
		
		RealMatrix coefficients = new Array2DRowRealMatrix(outputVars*numPoints,outputVars*numPoints);
//		System.out.println(coefficients.toString());
		
		RealVector constants = new ArrayRealVector(xy.length);

		
		for(int i=0;i < xy.length;i++ ){
//			System.out.println(xy[i][1]);
			constants.setEntry(i,xy[i][1]);
			for (int n = 0;n < coef.length;n++){
				String s = "",s1="";
				for(int l = 0;l < coef[n].length;l++ ){
//					s += coef[n][l]+"*X^" + l+" + ";
//					s1 += (i+n+1)+","+(l+1)+","+(coef[n][l]*Math.pow(xy[i][0],l))+ "  ";
					s1 += (coef[n][l]*Math.pow(xy[i][0],l))+ "  ";
					coefficients.setEntry(coef.length*i+n,l,coef[n][l]*Math.pow(xy[i][0],l));
					
				}
//				s += coef[n][coef[n].length-1]+"*X^" + (coef[n].length-1);
//				s += (i+n+1)+","+(l+1)+","+(coef[n][l]*Math.pow(xy[i][0],l));
				
//				System.out.println(s);
//				System.out.println("S1 "+s1);
//				System.out.println(coefficients.toString());


			}		

		}
		
//		System.out.println(coefficients.toString());
//		System.out.println(constants.toString());
		DecompositionSolver solver = new LUDecomposition(coefficients).getSolver();
		RealVector solution = solver.solve(constants);	
		
//		for (int n=0;n<solution.getDimension();n++){	
//			System.out.println(solution.getEntry(n));
//		}

		
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		CreatePolynomial cp = new CreatePolynomial(15,4);
		
		cp.displayEquations();
//		cp.createMatrix(new double[][] {{2,2},{4,4},{1,5},{6,9}});
	}

}
